View Javadoc
1   package edu.jiangxin.apktoolbox.android.dumpsys;
2   
3   import edu.jiangxin.apktoolbox.android.dumpsys.tojson.IDumpsys2Json;
4   import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
5   import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel;
6   import edu.jiangxin.apktoolbox.utils.Constants;
7   import org.apache.commons.exec.CommandLine;
8   import org.apache.commons.exec.DefaultExecutor;
9   import org.apache.commons.exec.PumpStreamHandler;
10  import org.apache.commons.io.FileUtils;
11  import org.apache.commons.io.FilenameUtils;
12  import org.apache.commons.lang3.StringUtils;
13  import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
14  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
15  import org.fife.ui.rtextarea.RTextScrollPane;
16  
17  import javax.swing.*;
18  import java.awt.*;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.ActionListener;
21  import java.io.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.Serial;
25  import java.text.SimpleDateFormat;
26  import java.util.Date;
27  
28  public class DumpsysPanel extends EasyPanel {
29      @Serial
30      private static final long serialVersionUID = 1L;
31  
32      private JPanel dumpsysPanel;
33  
34      private FilePanel dumpsysTargetDirPanel;
35  
36      private JPanel dumpsysOptionPanel;
37      private JPanel dumpsysTypeChoosePanel;
38  
39      private JPanel dumpsysOperationPanel;
40      private JButton dumpsysStartButton;
41  
42      private JPanel analysisPanel;
43      private FilePanel analysisFilePanel;
44      private RSyntaxTextArea analysisOutputTextArea;
45      private RTextScrollPane analysisOutputScrollPane;
46  
47      private JPanel analysisOperationPanel;
48  
49      public DumpsysPanel() {
50          super();
51      }
52  
53      @Override
54      public void initUI() {
55          setPreferredSize(new Dimension(700, 400));
56  
57          JTabbedPane tabbedPane = new JTabbedPane();
58          add(tabbedPane);
59  
60          createDumpsysPanel();
61          tabbedPane.addTab("Dumpsys", null, dumpsysPanel, "Dumpsys");
62  
63          createAnalysisPanel();
64          tabbedPane.addTab("Analysis", null, analysisPanel, "Analysis");
65      }
66  
67      @Override
68      public void afterPainted() {
69          super.afterPainted();
70          dumpsysTargetDirPanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysTargetDirPanel");
71          analysisFilePanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysAnalysisFilePanel");
72      }
73  
74      private void createDumpsysPanel() {
75          dumpsysPanel = new JPanel();
76          BoxLayout layout = new BoxLayout(dumpsysPanel, BoxLayout.Y_AXIS);
77          dumpsysPanel.setLayout(layout);
78          dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
79  
80          createDumpsysTargetDirPanel();
81          dumpsysPanel.add(dumpsysTargetDirPanel);
82          dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
83  
84          createDumpsysOptionPanel();
85          dumpsysPanel.add(dumpsysOptionPanel);
86          dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
87  
88          createDumpsysOperationPanel();
89          dumpsysPanel.add(dumpsysOperationPanel);
90      }
91  
92      private void createAnalysisPanel() {
93          analysisPanel = new JPanel();
94          BoxLayout layout = new BoxLayout(analysisPanel, BoxLayout.Y_AXIS);
95          analysisPanel.setLayout(layout);
96          dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
97  
98          createAnalysisTargetFilePanel();
99          analysisPanel.add(analysisFilePanel);
100         analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
101 
102         createAnalysisOutputPanel();
103         analysisPanel.add(analysisOutputScrollPane);
104         analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
105 
106         createAnalysisOperationPanel();
107         analysisPanel.add(analysisOperationPanel);
108     }
109 
110     private void createAnalysisTargetFilePanel() {
111         analysisFilePanel = new FilePanel("Analysis File");
112         analysisFilePanel.initialize();
113         analysisFilePanel.setFileSelectionMode(JFileChooser.FILES_ONLY);
114     }
115 
116     private void createAnalysisOutputPanel() {
117         analysisOutputTextArea = new RSyntaxTextArea();
118         analysisOutputTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON);
119         analysisOutputTextArea.setCodeFoldingEnabled(true);
120         analysisOutputTextArea.setEditable(false);
121 
122         analysisOutputScrollPane = new RTextScrollPane(analysisOutputTextArea);
123         analysisOutputScrollPane.setPreferredSize(new Dimension(400, 250));
124     }
125 
126     private void createAnalysisOperationPanel() {
127         analysisOperationPanel = new JPanel();
128         BoxLayout boxLayout = new BoxLayout(analysisOperationPanel, BoxLayout.X_AXIS);
129         analysisOperationPanel.setLayout(boxLayout);
130 
131         JButton analysisStartButton = new JButton("Start");
132         analysisStartButton.setEnabled(true);
133         analysisStartButton.addActionListener(new AnalysisStartButtonActionListener());
134         analysisOperationPanel.add(analysisStartButton);
135     }
136 
137     private void createDumpsysTargetDirPanel() {
138         dumpsysTargetDirPanel = new FilePanel("Target Directory");
139         dumpsysTargetDirPanel.initialize();
140         dumpsysTargetDirPanel.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
141     }
142 
143     private void createDumpsysOptionPanel() {
144         dumpsysOptionPanel = new JPanel();
145         BoxLayout boxLayout = new BoxLayout(dumpsysOptionPanel, BoxLayout.Y_AXIS);
146         dumpsysOptionPanel.setLayout(boxLayout);
147 
148         JPanel dumpsysCheckboxPanel = new JPanel();
149         dumpsysCheckboxPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
150 
151         JCheckBox allSelectedCheckBox = new JCheckBox("Select All");
152         allSelectedCheckBox.setSelected(false);
153         allSelectedCheckBox.addActionListener(e -> {
154             boolean selected = allSelectedCheckBox.isSelected();
155             if (selected) {
156                 Component[] components = dumpsysTypeChoosePanel.getComponents();
157                 for (Component component : components) {
158                     if (component instanceof JCheckBox checkBox) {
159                         checkBox.setSelected(true);
160                         checkBox.setEnabled(false);
161                     }
162                 }
163             } else {
164                 Component[] components = dumpsysTypeChoosePanel.getComponents();
165                 for (Component component : components) {
166                     if (component instanceof JCheckBox checkBox) {
167                         checkBox.setSelected(false);
168                         checkBox.setEnabled(true);
169                     }
170                 }
171             }
172 
173         });
174         dumpsysCheckboxPanel.add(allSelectedCheckBox);
175 
176         dumpsysTypeChoosePanel = new JPanel();
177         dumpsysTypeChoosePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
178         dumpsysTypeChoosePanel.setPreferredSize(new Dimension(500, 200));
179 
180         JCheckBox inputCheckBox = new JCheckBox("input");
181         inputCheckBox.setSelected(true);
182         JCheckBox windowCheckBox = new JCheckBox("window");
183         dumpsysTypeChoosePanel.add(windowCheckBox);
184         JCheckBox SurfaceFlingerCheckBox = new JCheckBox("SurfaceFlinger");
185         dumpsysTypeChoosePanel.add(SurfaceFlingerCheckBox);
186         JCheckBox activityCheckBox = new JCheckBox("activity");
187         dumpsysTypeChoosePanel.add(activityCheckBox);
188         JCheckBox accessibilityCheckBox = new JCheckBox("accessibility");
189         dumpsysTypeChoosePanel.add(accessibilityCheckBox);
190         JCheckBox packageCheckBox = new JCheckBox("package");
191         dumpsysTypeChoosePanel.add(packageCheckBox);
192         JCheckBox alarmCheckBox = new JCheckBox("alarm");
193         dumpsysTypeChoosePanel.add(alarmCheckBox);
194         JCheckBox inputMethodCheckBox = new JCheckBox("input_method");
195         dumpsysTypeChoosePanel.add(inputMethodCheckBox);
196         JCheckBox sensorServiceCheckBox = new JCheckBox("sensorservice");
197         dumpsysTypeChoosePanel.add(sensorServiceCheckBox);
198         JCheckBox accountCheckBox = new JCheckBox("account");
199         dumpsysTypeChoosePanel.add(accountCheckBox);
200         JCheckBox displayCheckBox = new JCheckBox("display");
201         dumpsysTypeChoosePanel.add(displayCheckBox);
202         JCheckBox powerCheckBox = new JCheckBox("power");
203         dumpsysTypeChoosePanel.add(powerCheckBox);
204         JCheckBox batteryCheckBox = new JCheckBox("battery");
205         dumpsysTypeChoosePanel.add(batteryCheckBox);
206 
207         dumpsysOptionPanel.add(dumpsysCheckboxPanel);
208         dumpsysOptionPanel.add(new JSeparator(JSeparator.HORIZONTAL));
209         dumpsysOptionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
210         dumpsysOptionPanel.add(dumpsysTypeChoosePanel);
211     }
212 
213     private void createDumpsysOperationPanel() {
214         dumpsysOperationPanel = new JPanel();
215         BoxLayout boxLayout = new BoxLayout(dumpsysOperationPanel, BoxLayout.X_AXIS);
216         dumpsysOperationPanel.setLayout(boxLayout);
217 
218         dumpsysStartButton = new JButton("Start");
219         dumpsysStartButton.setEnabled(true);
220         dumpsysStartButton.addActionListener(new DumpsysStartButtonActionListener());
221         dumpsysOperationPanel.add(dumpsysStartButton);
222     }
223 
224     class DumpsysStartButtonActionListener implements ActionListener {
225         @Override
226         public void actionPerformed(ActionEvent e) {
227             dumpsysStartButton.setEnabled(false);
228             File targetDirFile = dumpsysTargetDirPanel.getFile();
229             if (!targetDirFile.exists()) {
230                 JOptionPane.showMessageDialog(getFrame(), "Target directory does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE);
231                 return;
232             }
233 
234             String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date(System.currentTimeMillis()));
235             File targetDirWithTimestamp = new File(targetDirFile, timeStamp);
236 
237             if (!targetDirWithTimestamp.exists()) {
238                 boolean success = targetDirWithTimestamp.mkdirs();
239                 if (!success) {
240                     JOptionPane.showMessageDialog(getFrame(), "Create target directory failed!", "ERROR", JOptionPane.ERROR_MESSAGE);
241                     return;
242                 }
243             }
244 
245             Component[] components = dumpsysTypeChoosePanel.getComponents();
246             for (Component component : components) {
247                 if (component instanceof JCheckBox checkBox) {
248                     String dumpsysType = checkBox.getText();
249                     if (checkBox.isSelected()) {
250                         dumpsysAndSaveToFile(dumpsysType, targetDirWithTimestamp);
251                     }
252                 }
253             }
254             dumpsysStartButton.setEnabled(true);
255         }
256 
257         private void dumpsysAndSaveToFile(String dumpsysType, File targetDir) {
258             String command = "adb shell dumpsys " + dumpsysType;
259             CommandLine cmdLine = CommandLine.parse(command);
260             DefaultExecutor executor = DefaultExecutor.builder().get();
261             try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
262                  ByteArrayOutputStream errorStream = new ByteArrayOutputStream();) {
263                 PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, errorStream);
264                 executor.setStreamHandler(streamHandler);
265                 int exitValue = executor.execute(cmdLine);
266                 if (exitValue == 0) {
267                     String result = outputStream.toString().trim();
268                     if (StringUtils.isNotEmpty(result)) {
269                         File outputFile = new File(targetDir, dumpsysType + ".dumpsys");
270                         FileUtils.writeStringToFile(outputFile, result, "UTF-8");
271                     }
272                 } else {
273                     String errorResult = errorStream.toString().trim();
274                     if (StringUtils.isNotEmpty(errorResult)) {
275                         File errorFile = new File(targetDir, dumpsysType + ".error");
276                         FileUtils.writeStringToFile(errorFile, errorResult, "UTF-8");
277                     }
278                 }
279             } catch (IOException ex) {
280                 JOptionPane.showMessageDialog(getFrame(), "Execute command failed:\n" + ex.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
281             }
282         }
283     }
284 
285     class AnalysisStartButtonActionListener implements ActionListener {
286 
287         @Override
288         public void actionPerformed(ActionEvent e) {
289             File analysisFile = analysisFilePanel.getFile();
290             if (!analysisFile.exists()) {
291                 JOptionPane.showMessageDialog(getFrame(), "Analysis file does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE);
292                 return;
293             }
294             String fileNameWithoutExtension = FilenameUtils.getBaseName(analysisFile.getName());
295             String className = StringUtils.capitalize(fileNameWithoutExtension) + "2Json";
296             IDumpsys2Json dumpsys2Json = getDumpsys2JsonInstance(className);
297             if (dumpsys2Json != null) {
298                 String jsonResult = dumpsys2Json.dumpsys2Json(analysisFile);
299                 analysisOutputTextArea.setText(jsonResult);
300                 analysisOutputTextArea.setCaretPosition(0);
301             }
302         }
303 
304         private IDumpsys2Json getDumpsys2JsonInstance(String className) {
305             try {
306                 String fullClassName = "edu.jiangxin.apktoolbox.android.dumpsys.tojson." + className;
307                 Class<?> clazz = Class.forName(fullClassName);
308                 if (IDumpsys2Json.class.isAssignableFrom(clazz)) {
309                     return (IDumpsys2Json) clazz.getDeclaredConstructor().newInstance();
310                 } else {
311                     JOptionPane.showMessageDialog(getFrame(), "Class " + fullClassName + " does not implement IDumpsys2Json!", "ERROR", JOptionPane.ERROR_MESSAGE);
312                     return null;
313                 }
314             } catch (ClassNotFoundException e) {
315                 JOptionPane.showMessageDialog(getFrame(), "Class not found: " + className, "ERROR", JOptionPane.ERROR_MESSAGE);
316                 return null;
317             } catch (Exception e) {
318                 JOptionPane.showMessageDialog(getFrame(), "Error loading class: " + e.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
319                 return null;
320             }
321         }
322     }
323 }